/**
 * 在 inotify 中，文件的修改操作可能会触发多次事件，
 * 这是因为文件的修改操作可能涉及多个步骤，
 * 例如打开文件、写入数据、关闭文件等。每次操作都可能触发一个 IN_MODIFY 事件。
 * 为了减少这种多次触发事件的情况，
 * 可以在代码中增加一个过滤机制，确保只有在文件关闭后才认为文件被真正修改。
 * 可以通过监听 IN_CLOSE_WRITE 事件来实现这一点。
 * IN_CLOSE_WRITE 事件在文件被写入并关闭时触发，这样可以更准确地捕捉到文件的修改
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <signal.h>

#define EVENT_SIZE  ( sizeof (struct inotify_event) )
#define BUF_LEN     ( 1024 * ( EVENT_SIZE + 16 ) )

volatile sig_atomic_t stop = 0;

void handle_signal(int sig) {
    stop = 1;
    exit(0);
}

int main(int argc, char **argv) {
    int length, i = 0;
    int fd;
    int wd;
    char buffer[BUF_LEN];

    if (argc != 2) {
        printf("Usage: %s <filename>\n", argv[0]);
        return 1;
    }

    const char *filename = argv[1];

    // 初始化 inotify
    fd = inotify_init();
    if (fd < 0) {
        perror("inotify_init");
        return 1;
    }
    printf("inotify_init successful\n");

    // 添加要监视的文件 -- 修改文件会触发多次事件！！！
    wd = inotify_add_watch(fd, filename, IN_CLOSE_WRITE);
    if (wd < 0) {
        perror("inotify_add_watch");
        close(fd);
        return 1;
    }
    printf("inotify_add_watch successful for file: %s\n", filename);

    // 设置信号处理函数
    signal(SIGINT, handle_signal);
    signal(SIGTERM, handle_signal);

    // 监听事件
    while (!stop) {
        length = read(fd, buffer, BUF_LEN);
        if (length < 0) {
            if (errno == EINTR) {
                continue;
            }
            perror("read");
            break;
        }
        printf("read %d bytes from inotify fd:\n", length);
        for (size_t i = 0; i < length; i++) {
            printf("%02x ", buffer[i]);
        }
        printf("\n");

        // 读取文件
        if (0) {
            printf("Reading file: %s\n", filename);
            FILE *fp = fopen(filename, "r");
            if (fp == NULL) {
                perror("fopen");
                continue;
            }
            char line[1024];
            while (fgets(line, sizeof(line), fp) != NULL) {
                printf("line: %s", line);
            }
            fclose(fp);
        }

        i = 0;
        while (i < length) {
            struct inotify_event *event = (struct inotify_event *) &buffer[i];
            printf("Event: wd=%d, mask=0x%x, len=%d, name=%s\n", event->wd, event->mask, event->len, event->len ? event->name : "");
            if (event->len == 0) {
                printf("Event has no name (len=0)\n");
            }
            if (event->mask & IN_MODIFY) {
                printf("The file was modified.\n");
            }
            if (event->mask & IN_CLOSE_WRITE) {
                printf("The file was closed after write.\n");
            }
            i += EVENT_SIZE + event->len;
        }
        printf("==============================================\n");
    }

    // 关闭 inotify 实例
    inotify_rm_watch(fd, wd);
    close(fd);

    return 0;
}
